package njust.csc.aecore.entity

import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import njust.csc.aecore.utils.toLowerCaseAndStripAll

/**
 * @Author ZhouHang
 * @Date 24/5/28 周二
 */
@Serializable
data class APK(
    var absolutePath: String = "",
    var relativePath: String = "",
    var name: String = "",  // apk 的真实名称
    var packageName: String = "",
    var size: Int = -1,  // KB
    val versionInfo: VersionInfo = VersionInfo(),
    var permissions: Set<String> = emptySet(),
    val v1SignatureInfo: SignatureInfoV1 = SignatureInfoV1(),
    val v2SignatureInfo: SignatureInfoV2V3 = SignatureInfoV2V3(),
    val v3SignatureInfo: SignatureInfoV2V3 = SignatureInfoV2V3(),
    var icon: String = "",  // app 的 icon 图像 base64 编码

    // 分析结果存储
    var urls: MutableSet<String> = mutableSetOf(),
    var screenContent: String = "",  // 存储从xml中得到信息，用空格分隔
    var sdks: MutableSet<String> = mutableSetOf(),

    var type: ApkType = ApkType.WHITE,
    var level: DangerLevel = DangerLevel.WHITE,
    var isBad: Boolean = false,
    var time: Double = .0  // 当前 apk 静态分析耗费时间/s
) {
    /**
     * 将 apk 数据类序列化输出
     * @return String 序列化后为 Json 字符串的 apk 信息
     */
    fun dump(): String {
        return Json.encodeToString(this)
    }


    /**
     * 通过三个指标来确定该 APK 是否在白名单中，需要确保当前 APK 三个属性必存其一
     * @param packageName String 包名
     * @param appName String 应用名
     * @param md5 String 应用摘要
     * @return Pair<Boolean, Int> 匹配成功则返回 <True, 匹配属性的数量>
     */
    fun compareBy(packageName: String, appName: String, md5: String): Pair<Boolean, Int> {
        var result = false
        var possibility = 0
        if (packageName.isNotBlank() && this.packageName.isNotBlank()) {
            if (this.packageName == packageName) {
                result = true
                possibility += 1
            }
        }
        if (appName.isNotBlank() && this.name.isNotBlank()) {
            if (this.name == appName) {
                result = true
                possibility += 1
            }
        }
        if (md5.isNotBlank() && this.getMd5().isNotBlank()) {
            if (this.getMd5() == md5) {
                result = true
                possibility += 3  // md5 的权重最大
            }
        }
        return Pair(result, possibility)
    }

    /**
     * 从 v1/v2/v3 signature info 中寻找存在的 md5 值，若都不存在，则返回空字符串
     * @return String
     */
    fun getMd5(): String {
        if (v1SignatureInfo.certification.md5.isNotBlank())
            return v1SignatureInfo.certification.md5.toLowerCaseAndStripAll()
        if (v2SignatureInfo.certification.md5.isNotBlank())
            return v2SignatureInfo.certification.md5.toLowerCaseAndStripAll()
        if (v3SignatureInfo.certification.md5.isNotBlank())
            return v3SignatureInfo.certification.md5.toLowerCaseAndStripAll()
        return ""
    }
}

@Serializable
data class VersionInfo(
    var versionCode: Int = -1,
    var versionName: String = "",

    var minSdkVersion: Int = -1,
    var targetSdkVersion: Int = -1,
    var compileSdkVersion: Int = -1
)

@Serializable
data class SignatureInfoV1 (
    var name: String = "",
    var signatureFileName: String = "",
    var certification: Certification = Certification(),
)

@Serializable
data class SignatureInfoV2V3 (
    var index: Int = -1,
    var certification: Certification = Certification()
)

@Serializable
data class Certification (
    var subject: String = "",
    var signatureType: String = "",
    var md5: String = "",
    var sha1: String = "",
    var sha256: String = "",
)

enum class ApkType(s: String) {
    WHITE("white"),
    BLACK("black"),
    GAMBLE("gamble"),
    SCAM("scam"),
    SEX("sex"),
}

enum class DangerLevel(s: String) {
    RED("red"),
    YELLOW("yellow"),
    WHITE("white")
}


@Serializable
data class APKSimple (
    var name: String = "",
    var packageName: String = "",
    var size: Int = -1,  // KB
    val versionInfo: VersionInfo = VersionInfo(),
    var permissions: Set<String> = emptySet(),

    var subject: String = "",
    var signatureType: String = "",
    var md5: String = "",

    var urls: MutableSet<String> = mutableSetOf(),
)
